/*
 * WidgetContainer.java
 *
 * Created on 21 October 2007, 13:07
 *
 * Modified on 10 Feb 2008, 
 * New requirement that the WidgetContainer can now contain WidgetContainer(s) just like it can 
 * for AbsWidgets - instead of creating a new class to handle what would essentially be the same task 
 * as this I have modified this class to be able to hold WidgetContainers and AbsWidgets (Widgets). 
 * To realise this the IWidget class (formally known as the IView class) has been extended to provide 
 * a generic interface for both the WidgetContainer class and the AbsWidget class. Therefore all AbsWidget
 * handling methods now interact with the IWidget interface instead of the AbsWidget class - along with some 
 * changes to keyhandling etc and a few new methods to provide WidgetLooping etc etc 
 * 
 * 
 */

package net.ubinow.mflex.baseui;


import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;

import net.ubinow.mflex.data.IDataItem;
import net.ubinow.mflex.events.AbsEventPublisher;
import net.ubinow.mflex.events.Event;
import net.ubinow.mflex.events.IEventSubscriber;
import net.ubinow.mflex.util.Constants;
import net.ubinow.mflex.util.MiscUtil;
import net.ubinow.mflex.widgets.TabView;

/**
 *
 * @author Josh
 */
public class WidgetContainer extends AbsEventPublisher implements IWidget, IEventSubscriber {            
    
    protected static final int MOVEMENT_DOWN  = 0;
    protected static final int MOVEMENT_UP    = 1;
    protected static final int MOVEMENT_NONE  = 2;
    
    protected String _id = Constants.UNDEFINED; 
    protected int _x = 0;
    protected int _y = 0;
    protected int _offsetY = 0; // used when scrolling the window
    protected int _finalOffsetY = 0;
    protected int _width = 0;
    protected int _height = 0;
    
    protected int _bgColour = -1;
    protected Image _bgImage = null;
    
    protected WFont _font = null;
    
    ///
    // widget fields; the body/contents of the widget container
    ///
    protected IWidget[] _widgets = null;
    protected int _selectedWidget = -1;
    protected int _widgetCount = 0;
    
    protected int _paddingY = 0; // horizontal padding
    protected int _paddingX = 0; // vertical padding
    protected int _widgetPaddingH = 0; // padding
    
    protected int _horizontalAlignment = -1; // alignment of the widget within the column
    protected int _layout = -1;
    
    protected int _columns = 1; // number of widgets that will be placed horizontally on one line
    
    protected ITransistion _transitionManager = null;
    protected int _viewingState = Constants.VIEWING_STATE_HIDING;
    protected boolean _hide = false;
    protected boolean _removingWidget = false; // set to true when removing a widget and we're wanting to realign everything once the widget has been transitioned out'
    
    protected MiscUtil _miscUtil = null;
    
    protected int _maxLayers = 1; // number of times we need to iterate through the controls to render all layers 
    
    protected boolean _focus = false;         
    
    /** 
     * set this to true if this is the main container - ie you're wanting to loop through all components when the top 
     * or bottom is reached else false to return the control to the main/parent container 
     */
    protected boolean _loopingWidgetNav = true;
    
    /**
     * Creates a new instance of WidgetContainer
     */
    public WidgetContainer(){
        _miscUtil = MiscUtil.getInstance();        
        init();
    }
    
    public WidgetContainer( int x, int y, int width, int height ) {
        _x = x;
        _y = y;
        _width = width;
        _height = height;             
        _miscUtil = MiscUtil.getInstance();        
        init();
    }
    
    protected void init(){
        _horizontalAlignment = Constants.ALIGN_TOP | Constants.ALIGN_LEFT;
        _layout = Constants.LAYOUT_FLOW;
        
        // set up the default paddings
        _paddingY = Constants.AD_WIDGET_PADDING_H;
        _paddingX = Constants.AD_WIDGET_PADDING_V;
        _widgetPaddingH = Constants.AD_WIDGET_PADDING;
        
        show();
    }
    
    //<editor-fold defaultstate="collapsed" desc="widget container methods">
    
    public void setColumns( int col ){
        _columns = col;
    }
    
    public int getColumns(){
        return _columns;
    }
    
    public void setTransition( ITransistion transition ){
        if( transition == null )
            return; 
        
        _transitionManager = transition;
        
        _transitionManager.addEventSubscriber( Event.EVT_TRANSITION_IN_FINISHED, this );
        _transitionManager.addEventSubscriber( Event.EVT_TRANSITION_OUT_FINISHED, this );
        
    }
    
    public void show(){
        if( _transitionManager != null ){
            _transitionManager.prepareTransIn( _widgets );
            setViewingState( Constants.VIEWING_STATE_IN_TRANSITION );
        } else{
            setViewingState( Constants.VIEWING_STATE_SHOWING );
        }
    }
    
    public void hide(){
        _hide = true;       
        
        if( _transitionManager != null ){
            _transitionManager.prepareTransOut( _widgets );
            setViewingState( Constants.VIEWING_STATE_IN_TRANSITION );
        } else{
            setViewingState( Constants.VIEWING_STATE_HIDING );
        }
    }
    
    public void setViewingState( int state ){
        _viewingState = state;
        
        if( _viewingState == Constants.VIEWING_STATE_SHOWING ){
            notifySubscribers( getEvet( Event.EVT_VIEW_SHOWING ) );
        } else if ( _viewingState == Constants.VIEWING_STATE_HIDING ){
        	notifySubscribers( getEvet( Event.EVT_VIEW_HIDING ) );
        }
    }    
    
    //</editor-fold>
    
    //<editor-fold defaultstate="collapsed" desc="widget management methods">        
    
    /** set the widget alignment; also sets the layout type to LAYOUT_ALIGNED **/ 
    public void setAlignment( int alignment ){
        _horizontalAlignment = alignment; 
        _layout = Constants.LAYOUT_ALIGNED;
        reinitSizeAndWidgetPositions(); 
    }
    
    /**
     * if flow layout then widgets will for squashed together (depending on number of columns and padding)
     * if aligned layout then components will be positioned depending on the calculated column width 
     * param layout: either LAYOUT_FLOW or LAYOUT_ALIGNED
     **/ 
    public void setLayout( int layout ){
        _layout = layout;
    }
    
    public void setWidgets( AbsWidget[] widgets ){
        _widgets = widgets;
        _widgetCount = _widgets.length;
        
        reinitSizeAndWidgetPositions();
    }
    
    public void addWidget( IWidget widget, boolean transition ){
        
        if( _widgets == null ){
            _widgets = new IWidget[Constants.AD_ARRAY_EXPAND_FACTOR];
            _widgets[0] = widget;
            _widgetCount++;
        } else{
            int len = _widgets.length;
            
            // find a place holder else expand the array
            if( _widgetCount == len ){
                // expand array
                IWidget[] tmp = new IWidget[len+Constants.AD_ARRAY_EXPAND_FACTOR];
                System.arraycopy( _widgets, 0, tmp, 0, len );
                _widgets = tmp;
            }
            
            _widgets[_widgetCount] = widget;
            _widgetCount++;
        }
        
        reinitSizeAndWidgetPositions();
        
        if( _transitionManager != null && transition ){
            _transitionManager.prepareTransIn( new IWidget[]{widget} );
        }
        
        // listen to events that affect the gui 
        widget.addEventSubscriber( Event.EVT_GUI_OUT_OF_BOUNDS, this );
        widget.addEventSubscriber( Event.EVT_GUI_RESIZE, this );
        widget.addEventSubscriber( Event.EVT_OFFSET_Y_CHANGE, this );
        
//        // if the first widget then set to selected
//        if( _selectedWidget == -1 && widget.isSelectable() ){
//            setFocus( widget ); 
//        } 
    }
    
    public void insertWidget( int index, IWidget widget, boolean transition ){
        if( _widgets == null ){
            _widgets = new IWidget[Constants.AD_ARRAY_EXPAND_FACTOR];
            _widgets[0] = widget;
            _widgetCount++;
        } else{
            int len = _widgets.length;
            
            // find a place holder else expand the array
            if( _widgetCount == len ){
                // expand array
                IWidget[] tmp = new IWidget[len+Constants.AD_ARRAY_EXPAND_FACTOR];
                System.arraycopy( _widgets, 0, tmp, 0, len );
                _widgets = tmp;
            }
            
            // move all items along
            for( int i=_widgetCount; i>=index; i-- ){
                _widgets[_widgetCount] = _widgets[_widgetCount-1];
            }
            
            // now insert item
            _widgets[index] = widget;
            _widgetCount++;
        }
        
        reinitSizeAndWidgetPositions();
        
        if( _transitionManager != null && transition ){
            _transitionManager.prepareTransIn( new IWidget[]{widget} );
        }
    }
    
    public void removeWidget( IWidget widget, boolean transition ){
        if( _widgets == null )
            return;
        
        int index = -1;
        
        for( int i=_widgetCount-1; i>=0; i-- ){
            if( _widgets[i].equals( widget ) ){
                index = i;
                break;
            }
        }
        
        // item not found
        if( index == -1 )
            return;
        
        for( int i=index; i<_widgetCount; i++ ){
            if( (i+1) < _widgetCount ){
                _widgets[i] = _widgets[i+1];
            } else{
                _widgets[i] = null;
            }
        }
        
        _widgetCount--;
        
        if( _transitionManager != null && transition ){
            _removingWidget = true;
            _transitionManager.prepareTransIn( new IWidget[]{widget} );
        } else{
            reinitSizeAndWidgetPositions();
        }
        
    }
    
    public void setFocus( IWidget widget ){        
        
        if( !widget.isSelectable() )
            return;
        
        int index = -1;
        
        for( int i=_widgetCount-1; i>=0; i-- ){
            if( _widgets[i].equals( widget ) ){
                index = i;
                break;
            }
        }
        
        if( index == -1 )
            return;                 
        
        _selectedWidget = index;
        widget.setFocus( true ,-1 );
    }
    
    public IWidget getWidget( String id ){
        if( _widgets == null || _widgetCount == 0 )
            return null;
        
        for( int i=_widgetCount-1; i>=0; i-- ){
            if( _widgets[i] == null || _widgets[i].getID() == null )
                continue; 
            
            if( _widgets[i].getID().equals( id ) )
                return _widgets[i]; 
            
        }
        
        return null;
    }
    
    protected int _widgetsHeight = 0; // accumulated height of all of the widgets    
    
    /**
     * called everytime a widget is added or removed; used to realign the view; mimics flow layout
     **/        
    protected void reinitSizeAndWidgetPositions( int offsetX, int offsetY ){  
        if( _widgetCount <= 0 )
            return;
        
        int x = offsetX;
        int y = offsetY;
        int colHeight = 0;
        int colWidth = 0;
        
        _widgetsHeight = 0;        
        
        if( _layout == Constants.LAYOUT_ALIGNED )
            colWidth = _width/_columns;                        
        
        for( int i=0; i<_widgetCount; i++ ){
            x = offsetX;
            colHeight = 0;            
            
            if( _widgets[i] == null )
                continue;
                                    
            for( int c=0; c<_columns; c++ ){
                if( _widgets[i].getTotalHeight() > colHeight )
                    colHeight = _widgets[i].getTotalHeight();
                
                // if two columns check to see if we can fit the next widget onto the same line
                if( c > 0 ){
                    if( x+_widgets[i].getTotalWidth() > _width ){
                        c = _columns;
                        break;
                    }
                }
                
                // TODO: support vertical alignment
                
                if( _layout == Constants.LAYOUT_FLOW ){
//                    System.out.println( "Widget: " + _widgets[i] + ", index: " + i + ", colHeight: " + colHeight 
//                            + ", x: " + x + ", y: " + y + ", widget height " + _widgets[i].getTotalHeight() + 
//                            " widget height2: " + _widgets[i].getHeight());
                    _widgets[i].setOrgPos( x, y );                    
                    x+= _widgets[i].getTotalWidth();
                }
                else{
                    // TODO: provide a more versitile way of positioning the widgets 
                    x += colWidth;
                }
                
                // check out the layer 
                if( _widgets[i].maxLayers() > _maxLayers )
                    _maxLayers = _widgets[i].maxLayers(); 
                                
            }
            
            y += colHeight+_widgetPaddingH;
            _widgetsHeight += colHeight+_widgetPaddingH;
            
        }
    }
    protected void reinitSizeAndWidgetPositions(){ 
        if( _widgets == null || _widgetCount == 0 )
            return; 
        
        // TODO: find a more elegant solution to this - one with better performance and more scalable
        if( _widgets[0] instanceof TabView )
            reinitSizeAndWidgetPositions( 0, 0 );
        else
            reinitSizeAndWidgetPositions( _paddingX, _paddingY );
    }
    
    //</editor-fold>
    
    //<editor-fold defaultstate="collapsed" desc="IWidget implementation">
    public void setFont( WFont font ){
        _font = font;
    }
    
    public WFont getFont(){
        return _font;
    }
    
    public void setPos(int x, int y) {
        _x = x; 
        _y = y;
    }
    
    public int getX() {
        return _x;
    }
    
    public void setX(int x) {
        _x = x;
    }
    
    public int getBottomY( boolean includeHeight ){
        if( _widgets == null || _widgetCount == 0 && _widgets[_widgetCount-1] != null )
            return _y+_offsetY; 
        else
            return _widgets[_widgetCount-1].getY()+_y+_offsetY;
    }
    
    public int getBottomY(){
        return getBottomY( false );
    }
    
    public int getY() {
        return + _y;
    }
    
    public void setY(int y) {
        _y = y;
    }
    
    private int _orgX = -1;
    private int _orgY = -1;
    
    public void setOrgPos( int x, int y ){
        _orgX = x; 
        _orgY = y; 
        resetPos();
    }
    
    public void resetPos(){
        _x = _orgX;
        _y = _orgY;
    } 
    
    public int getOrgX(){
        return _orgX;
    }
    public int getOrgY(){
        return _orgY;
    }
    
    public int getWidth() {
        return _width;
    }
    
    public int getTotalWidth(){
        return _width;
    }
    
    public void setWidth(int w) {
        _width = w;
    }       
    
    public int getHeight() {
        return _height;
    }
    
    public int getTotalHeight(){
        return _height;
    }
    
    public void setHeight(int h) {
        _height = h;
    }
    
    public void setBG( Image bg ){
        _bgImage = bg; 
    }
    
    public void setBG( int colour ){
        _bgColour = colour;
    }
    
     public int getOffsetY(){
        return _offsetY;
    }
    
    public void setOffsetY( int offsetY ){
        _offsetY = offsetY;
//        notifyListeners( getEvet( Event.EVT_OFFSET_Y_CHANGE, Integer.toString( offsetY ) ) );
    }
    
    public void cycle() {
        if( _transitionManager != null && _viewingState == Constants.VIEWING_STATE_IN_TRANSITION ) {
            _transitionManager.move();
        }                
        
        if( getOffsetY() != _finalOffsetY ){
            setOffsetY( getOffsetY() + _miscUtil.calcHalfDistance( _finalOffsetY, getOffsetY() ) );
        }
       
        // TODO: should we only cycle the selected widget or shall we cycle or widgets?         
        if( _widgets != null && _selectedWidget >= 0 && _selectedWidget < _widgetCount )
            _widgets[_selectedWidget].cycle();
       
        // cycle any widgets that have been flagged ie those that require animations etc 
        for( int i=_widgetCount-1; i>=0; i-- ){
            if( _widgets[i] != null && i != _selectedWidget && _widgets[i].isAnimated() ){
                _widgets[i].cycle();
            }
        }
    }       
    
    public void paint( Graphics g, int offsetX, int offsetY, int layer ) {
        this.paint( g, offsetX, offsetY, layer, false );
    }
    
    protected int prevX = 0;
    protected int prevY = 0;
    protected int prevWidth = 0;
    protected int prevHeight = 0;
    
    public void paint( Graphics g, int offsetX, int offsetY, int layer, boolean clipped ) {
        if( !isVisible() )
            return;
        
        // TODO: handle scrolling here!!! 
        
//        MiscUtil.getInstance().clip( g, _x + offsetX, _y + offsetY, _width, _height ); 
        if( !clipped ){
            prevX = g.getClipX();
            prevY = g.getClipY();
            prevWidth = g.getClipWidth();
            prevHeight = g.getClipHeight();
            g.setClip( _x + offsetX, _y + offsetY, _width, _height );
        }
        
        if( _bgColour != -1 ){
            g.setColor( _bgColour );
            g.drawRect( _x + offsetX, _y + offsetY + _offsetY, _width, _height );
        }
        
        if( _bgImage != null ){
            g.drawImage( _bgImage, _x + offsetX, _y + offsetY + _offsetY, Constants.GFX_ALIGN_TOP_LEFT );
        }                
                        
        for( int l=1; l<=_maxLayers; l++ ){
            for( int i=0; i<_widgetCount; i++ ){
                if( _widgets[i].maxLayers() >= l )                    
                    _widgets[i].paint( g, _x + offsetX, _y+_offsetY + offsetY, l );
            }
        }
        
        if( !clipped )
            g.setClip( prevX, prevY, prevWidth, prevHeight );
//        MiscUtil.getInstance().releaseClip( g ); 
    }
    
    /**
     * @return: true if the key has been handled; else return false to indicate that the parent must handle the key pressed
     **/
    public boolean handleKeyPressed( int action, int keyCode ) {

        if( _widgets == null )
            return false;
        
        if( _selectedWidget != -1 && _widgets[_selectedWidget].handleKeyPressed( action, keyCode ) )
            return true;
        
        if( action == Canvas.DOWN ){
            return moveFocusDown( false, Constants.DIRECTION_DOWN );
        } else if( action == Canvas.UP ){
            return moveFocusUp( false );
        } else if( action == Canvas.LEFT ){
            return moveFocusUp( false );
        } else if( action == Canvas.RIGHT ){
            return moveFocusDown( false, Constants.DIRECTION_DOWN );
        }        
        return false;
    } 
    
    public boolean handleKeyReleased( int action, int keyCode ){
        if( _widgets == null )
            return false;
        
        if( _selectedWidget != -1 && _widgets[_selectedWidget].handleKeyReleased( action, keyCode ) )
            return true;
        
        return false; 
    }
    
    public String getID() {
        return _id; 
    }

    public void setID(String id) {
        _id = id; 
    }

    public int getPaddingW() {
        return _paddingX;
    }

    public void setPaddingW(int padding) {
        _paddingX = padding; 
        reinitSizeAndWidgetPositions();
    }

    public int getPaddingH() {
        return _paddingY; 
    }

    public void setPaddingH(int padding) {
        _paddingY = padding; 
        reinitSizeAndWidgetPositions();
    }               
    
    /** determines whether this IView should be rendered or not **/ 
    public boolean isVisible() {
        if( _viewingState  == Constants.VIEWING_STATE_HIDING )
            return false;
        else
            return true; 
    }
    
    public void setIsVisible( boolean isVisible ){
        if( isVisible )
            show();
        else
            hide(); 
    }
    
    public int maxLayers(){
        return 1; 
    }
    
    public boolean isSelectable(){
        return true; 
    }
    
    public void setFocus( boolean focus ,int direction ){
        _focus = focus;
        
        if( focus )
            notifySubscribers( getEvet( Event.EVT_ON_FOCUS ) );
        else
        	notifySubscribers( getEvet( Event.EVT_OFF_FOCUS ) );
        
        if( focus )
            moveFocusDown( false, direction );
        else{
            if( _selectedWidget != -1 && _widgets[_selectedWidget] != null ){
//                _widgets[_selectedWidget].setFocus( false ,Constants.DIRECTION_DOWN );
                _widgets[_selectedWidget].setFocus( false , direction );
                _selectedWidget = -1;
            }
        }
        
    }
    
    public void selectLastWidget(){
        _selectedWidget = _widgetCount-2; 
    }
    
    public boolean isAnimated(){
        return false; 
    }
    
    public boolean supportsTransitioning(){
        if( _widgets == null || _widgetCount <= 0 || _widgets[0] == null )
            return false;
        else
            return _widgets[0].supportsTransitioning();  
    }
    
    public int getWidgetCount(){
        return _widgetCount;
    }
    
    
    //</editor-fold>
    
    //<editor-fold defaultstate="collapsed" desc="other public methods"> 
    
    /** 
     * set this to true if this is the main container - ie you're wanting to loop through all components when the top 
     * or bottom is reached else false to return the control to the main/parent container 
     */
    public void setLoopingWidgetNav( boolean looping ){
        _loopingWidgetNav = looping;
    }
    
    public boolean getLoopingWidgetNav(){
        return _loopingWidgetNav;
    }
    
    //</editor-fold>
    
    //<editor-fold defaultstate="collapsed" desc="protected methods">            
    
    protected boolean moveFocusUp( boolean forceFullLoop ){
        boolean moved = false;
        int start = 0;
        int prevWIndex = _selectedWidget;
        
        if( (_selectedWidget == 0 || forceFullLoop) && _loopingWidgetNav )
            start = _widgetCount-1;
        else
            start = _selectedWidget-1;
        
        for( int i=start; i>=0; i-- ){
            if( _widgets[i] != null && _widgets[i].isSelectable() ){
                if( _selectedWidget != i ){
                    _widgets[_selectedWidget].setFocus( false , Constants.DIRECTION_UP );
                    _selectedWidget = i;
                    _widgets[_selectedWidget].setFocus( true , Constants.DIRECTION_UP );
                }                
                moved = true; 
                break;
            }
                                    
        }
        
        // wrap around if we haven't found one to focus  - this time startnging from zero 
        if( !forceFullLoop && !moved && _loopingWidgetNav )
                moved = moveFocusUp( true ); 
        else if( !forceFullLoop && !moved ) {
            // notify the container that we have reached the top of the widgets
        	notifySubscribers( getEvet( Event.EVT_OFF_FOCUS, Integer.toString( Constants.DIRECTION_DOWN ) ) );
        }
        
        if( moved ){
            adjustOffsetY( prevWIndex, _selectedWidget );
            return true;
        }
        else{
            return false; 
        }
    }
    
    protected int _previousSelectedWidget = -1;
    
    protected boolean moveFocusDown( boolean forceFullLoop, int direction ){
        boolean moved = false;
        int start = 0;
        int prevWIndex = _selectedWidget;
        
        if( (_selectedWidget >= _widgetCount-1 || forceFullLoop) && _loopingWidgetNav)
            start = 0;
        else
            start = _selectedWidget+1;
        
        for( int i=start; i<_widgetCount; i++ ){
            if( _widgets[i] != null && _widgets[i].isSelectable() ){
                if( _selectedWidget != i ){
                    if( _selectedWidget != -1 )
//                        _widgets[_selectedWidget].setFocus( false ,Constants.DIRECTION_DOWN );
                        _widgets[_selectedWidget].setFocus( false , direction );
                    
                    _selectedWidget = i;
//                    _widgets[_selectedWidget].setFocus( true ,Constants.DIRECTION_DOWN );
                    _widgets[_selectedWidget].setFocus( true , direction );
                }                
                moved = true; 
                break;
            }
                                    
        }        
        
        // wrap around if we haven't found one to focus  - this time startnging from zero 
        if( start > 0 && !moved && _loopingWidgetNav )
                moved = moveFocusDown( true, direction ); 
        else if( start > 0 && !moved ) {
            // notify the container that we have reached the bottom of the widgets
        	notifySubscribers( getEvet( Event.EVT_OFF_FOCUS, Integer.toString( Constants.DIRECTION_DOWN ) ) );
        }
        
        // now check to see that we can view the whole widget - if not then move this widget up 
        if( moved ){
            adjustOffsetY( prevWIndex, _selectedWidget );
            return true; 
        }                
        else{
            return false; 
        }
    }
    
    protected boolean moveFocusLeft(){
        return moveFocusUp( false );
    }
    
    public boolean moveFocusRight(){
        return moveFocusDown( false, Constants.DIRECTION_DOWN );
    }        
    
    protected void adjustOffsetY( int prevWIndex, int wIndex ){
        adjustOffsetY( prevWIndex, wIndex, true );
    }
    
    /** adjusts the offset Y to ensure that the selected widget is in view **/ 
    protected void adjustOffsetY( int prevWIndex, int wIndex, boolean amendToOffset ){        
        if( prevWIndex == wIndex )
            return;
        
        int movement = MOVEMENT_NONE;
        
        if( prevWIndex < wIndex ){
            movement = MOVEMENT_DOWN;
        }
        else if( prevWIndex > wIndex ){
            movement = MOVEMENT_UP;
        }
        
        if( outOfBounds( wIndex, movement ) ){ 
            
            if( movement == MOVEMENT_DOWN ){
                if( _selectedWidget == _widgetCount-1 && prevWIndex == 0 ){  
                    if( amendToOffset )
                        _finalOffsetY += getHeight() - getWidgetPosY( _selectedWidget );
                    else
                        _finalOffsetY = getHeight() - getWidgetPosY( _selectedWidget );
                }
                else{
                    if( amendToOffset )
                        _finalOffsetY += getRemainingWidgetHeight( _selectedWidget, movement );
                    else
                        _finalOffsetY = getRemainingWidgetHeight( _selectedWidget, movement );                    
                }
            }
            else if( movement == MOVEMENT_UP ){
                if( _selectedWidget == 0 )
                    _finalOffsetY = 0; 
                else{
                    if( amendToOffset )
                        _finalOffsetY += getRemainingWidgetHeight( _selectedWidget, movement );
                    else
                        _finalOffsetY = getRemainingWidgetHeight( _selectedWidget, movement );   
                }
            }
            
            notifySubscribers( getEvet(Event.EVT_OFFSET_Y_CHANGE, Integer.toString( _finalOffsetY ) ) );
        }        
    }
    
    /**
     * @param widget: widget to perform out of bounds (ie not visible on screen) check 
     **/ 
    protected boolean outOfBounds( int wIndex, int movement ){
        // if last widget and int view then return false         
        IWidget widget = _widgets[wIndex];         
        
        if( movement == MOVEMENT_DOWN )
            return ( getWidgetPosY(wIndex) + widget.getTotalHeight() ) > (_y + _height );
        else if( movement == MOVEMENT_UP )
            return getWidgetPosY(wIndex) < _y;
        
        return false; 
    }    
    
    protected int getRemainingWidgetHeight( int wIndex, int movement ){
        int sumOfHeight = 0;
        int previousY = 0;
        
        if( movement == MOVEMENT_DOWN ){
            for( int i=_selectedWidget; i<_widgetCount; i++ ){                                
                
                if( previousY != _widgets[i].getY() ){
                    sumOfHeight -= _widgets[i].getTotalHeight();
                }                              
            }
        }
        else if( movement == MOVEMENT_UP ){
            for( int i=_selectedWidget; i>=0; i-- ){
                if( previousY != _widgets[i].getY() ){
                    sumOfHeight += _widgets[i].getTotalHeight();
                }
            }
        }
        
        return sumOfHeight; 
    }
    
    /** 
     * @param wIndex: widget index (widget of interest) 
     **/ 
    protected int getWidgetPosY( int wIndex ){
//        System.err.println("getWidgetPosY = " + (_widgets[wIndex].getY() + _y + _offsetY) );
        return _widgets[wIndex].getBottomY() + _y + _offsetY;
    }
    
    //</editor-fold>
    
    //<editor-fold defaultstate="collapsed" desc="IEventListener implementation">
    public void handleEvent(Event evt) {
        if( evt._type == Event.EVT_TRANSITION_IN_FINISHED ){
            setViewingState( Constants.VIEWING_STATE_SHOWING );                        
        } else if( evt._type == Event.EVT_TRANSITION_OUT_FINISHED && _hide ){
            setViewingState( Constants.VIEWING_STATE_HIDING );
        } else if( evt._type == Event.EVT_TRANSITION_OUT_FINISHED && _removingWidget ){
            reinitSizeAndWidgetPositions(); // realign objects once a widget has been removed
            _removingWidget = false;
        }
        
        // capture out of bounds events and adjust the guis offset accordingly 
        else if( evt._type == Event.EVT_GUI_OUT_OF_BOUNDS ){
            int offset = Integer.parseInt( ((IDataItem[])evt._value)[0].getText() ); 
            _finalOffsetY -= offset;      
        }
        
        // capture any widget resizes and resize all of the widgets according to it's new size
        else if( evt._type == Event.EVT_GUI_RESIZE ){
            reinitSizeAndWidgetPositions();             
            if( _selectedWidget != -1 ){
                IWidget widget = _widgets[_selectedWidget];              
                
                if( widget.getBottomY() > (_y + _height - _offsetY) && ((Boolean)evt._value).booleanValue() && _finalOffsetY == _offsetY ){                    
                //if( widget.getBottomY() > (_y + _height - _offsetY)  ){                    
                    int offset = _height - widget.getBottomY( true );
                    _finalOffsetY = offset;                            
                }
            }            
        }
        
        else if( evt._type == Event.EVT_OFFSET_Y_CHANGE ){
            if( evt._value != null ){
                String offsetY = (String)evt._value;
                _finalOffsetY += Integer.parseInt( offsetY );
            }
        }
        
    }   
    
    // TODO: derive if widget container is active or not 
    //       possibly derive if the widget container is still active by time stamping everytime cycle gets called; 
    //       once this lapsed then set to inactive to hopefully free it up to get gargabe collected
    
    private boolean _isActiveSubscriber = true; 
    
    public boolean isActiveSubscriber(){
        return _isActiveSubscriber; 
    }
    
    public void setActiveSubscriber( boolean active ){
    	_isActiveSubscriber = active;
    }
    
//</editor-fold>              
    
}
